/**************************************** 
 * This Javascript program should be run with node.js
 * If a function is a property of an object, it is a method. When a function
 * which is a method is invoked, it is bound to the object. 
 * Following is how to invoke a method:
 * object.mymethod();
 * object["mymethod"]();
 ***************************************/
var myObj = {
	num: 0,
	addNum: function (value){
		this.num += ("number" === typeof value) ? value : 0;
	}
};

console.log("myObj.num="+myObj.num);
myObj.addNum(2);
console.log("myObj.num="+myObj.num);
myObj.addNum(3);
console.log("myObj.num="+myObj.num);

/**************************************************
If a function is not a property of an object, it's invoked as function.
When a function is invoked, it's bound to global this object.
To bind to the parent object, you need to rewrite your function a little
different.
***************************************************/
myObj.mult = function(a){
    var that = this;   // This will make the outer function object bound.
    var myInnerFunc = function(){
        console.log("Inside innerfunc num="+that.num);
        that.num = that.num * ((typeof a === 'number') ? a : 1);
        console.log("After mult num="+that.num);
    }
    myInnerFunc();
}
myObj.mult(2);
console.log("myObj.num="+myObj.num);

/***************************************************
If a function is invoked with a "new" prefix, a new instance of the 
object will be created with hidden link to its prototype value member.
"this" will be bound to that new object.
****************************************************/
//Here is how to create a constructor function.

var Obj = function(myStr){
	this.myStr = myStr;
	//If the return value not object, what does it do?
	return 12345;
}

//If we want to add a public method for all instances of myObj,
//add it to the prototype.

Obj.prototype.getString = function(){
	return this.myStr;
}

//Create instance must use the new operator to create new object.
var myObj = new Obj("hello");
console.log(myObj.getString());
//Calling function without new returns the actual return value.
console.log("Calling without new returns = "+Obj());
var myObj2 = new Obj("Hello, again");
console.log(myObj2.getString());


/************************************************
Additional methods can be applied to an existing
Javascript object by using function apply method.
Apply even works for the built-in object like numbers,
arrays, etc.
*************************************************/
function prefixMsg(){
	console.log("msg: "+this.myStr);
}

//Apply the new function
prefixMsg.apply(myObj);

function addNumbers(par1, par2){
	return par1+par2;
}

//Apply function takes two parameters
//The first is the object that bound to this,
//the second is the list of arguments passed to
//the functions.
var arrNum = [10, 5];
console.log("sum: "+addNumbers.apply(null, arrNum));

//When a function is called, the list of arguments can be observed
//using arguments array.
function printArgs(){
	var pos;
	for(pos = 0; pos < arguments.length; pos++){
		console.log("pos("+pos+")="+arguments[pos]);
	}
	console.log("Printing it again using for...in");
	for(n in arguments)
	{
		console.log("n="+n);
	}
}
printArgs("testing", 1, 2, 2.5, 3, "end");

//Function can throw exception
function thrower(){
	throw {
		msg: "Hello from function",
		details: "This is details of an exception thrown from thrower"
	}
	console.log("We should never see this message here");
}
try
{
	thrower(); //It should throw some exception
}
catch(e)
{
	console.log("We just caught something");
	console.log("msg: "+e.msg);
	console.log("details: "+e.details);
}

//Create a basic object.
var Record = function(name, age){
	this.name = name;
	this.age = age;
};

//You can augument basic types of Javascript.
//Some basic types are Object, Number, String, and Function.
//Let's make it easy to create methods for objects.
Object.prototype.method = function(name, func){
	this.prototype[name] = func;
	return this;
}
var person1 = new Record("John Doe", 22);

//Here we are augumenting all the objects with printNameAndAge
Object.method("printNameAndAge", function(){
	console.log("name: "+this.name);
	console.log("age: "+this.age);
});

person1.printNameAndAge();


